#include "check_room.h"
#include <stdio.h>
#include <pthread.h>
#include <stdint.h>
#include <unistd.h>
#include <time.h>

#define N_THREADS 4
#define N_WAKEUPS 100000

typedef struct { check_room_t* cr; } cr_thread_param_t;

void* worker_cr(void* arg) {
    cr_thread_param_t* p = (cr_thread_param_t*)arg;
    for(int i=0;i<N_WAKEUPS;i++)
        cr_wait(p->cr, -1);
    return NULL;
}

double benchmark_check_room() {
    check_room_t* cr = cr_create();
    pthread_t threads[N_THREADS];
    cr_thread_param_t params[N_THREADS];

    for(int i=0;i<N_THREADS;i++) {
        params[i].cr = cr;
        pthread_create(&threads[i], NULL, worker_cr, &params[i]);
    }

    struct timespec start, end;
    clock_gettime(CLOCK_MONOTONIC, &start);

    for(int i=0;i<N_WAKEUPS;i++)
        for(int t=0;t<N_THREADS;t++)
            cr_notify(cr);

    for(int i=0;i<N_THREADS;i++)
        pthread_join(threads[i], NULL);

    clock_gettime(CLOCK_MONOTONIC, &end);
    cr_destroy(cr);

    double elapsed = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec)/1e9;
    return elapsed;
}

/* ===================== pthread_cond reliable benchmark ===================== */
typedef struct {
    pthread_mutex_t mutex;
    pthread_cond_t cond;
    int counter;
} thread_cond_t;

typedef struct {
    thread_cond_t* threads;
    int index;
} thread_param_t;

void* worker_cond(void* arg) {
    thread_param_t* param = (thread_param_t*)arg;
    thread_cond_t* tc = &param->threads[param->index];

    for(int i=0;i<N_WAKEUPS;i++) {
        pthread_mutex_lock(&tc->mutex);
        while(tc->counter == 0)
            pthread_cond_wait(&tc->cond, &tc->mutex);
        tc->counter--;
        pthread_mutex_unlock(&tc->mutex);
    }
    return NULL;
}

double benchmark_pthread_cond() {
    thread_cond_t threads[N_THREADS];
    pthread_t tid[N_THREADS];
    thread_param_t params[N_THREADS];

    for(int i=0;i<N_THREADS;i++) {
        pthread_mutex_init(&threads[i].mutex, NULL);
        pthread_cond_init(&threads[i].cond, NULL);
        threads[i].counter = 0;
        params[i].threads = threads;
        params[i].index = i;
        pthread_create(&tid[i], NULL, worker_cond, &params[i]);
    }

    // 等待线程启动完成
    usleep(100000); // 0.1s

    struct timespec start, end;
    clock_gettime(CLOCK_MONOTONIC, &start);

    for(int i=0;i<N_WAKEUPS;i++) {
        for(int t=0;t<N_THREADS;t++) {
            pthread_mutex_lock(&threads[t].mutex);
            threads[t].counter++;
            pthread_cond_signal(&threads[t].cond);
            pthread_mutex_unlock(&threads[t].mutex);
        }
    }

    for(int i=0;i<N_THREADS;i++)
        pthread_join(tid[i], NULL);

    clock_gettime(CLOCK_MONOTONIC, &end);

    for(int i=0;i<N_THREADS;i++) {
        pthread_mutex_destroy(&threads[i].mutex);
        pthread_cond_destroy(&threads[i].cond);
    }

    double elapsed = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec)/1e9;
    return elapsed;
}

/* ===================== main ===================== */
int main() {
    printf("Benchmark N_THREADS=%d, N_WAKEUPS=%d\n", N_THREADS, N_WAKEUPS);

    double t1 = benchmark_check_room();
    printf("check_room: %.6f s\n", t1);

    double t2 = benchmark_pthread_cond();
    printf("pthread_cond reliable: %.6f s\n", t2);

    return 0;
}
